<!--
 * @Title: 抛物线运动模拟器
 * @Author: 红目香薰
 * @Date: 2025年7月23日
 * @Platform: HTML5/JavaScript
 * @Description: 基于Canvas的抛物线运动轨迹模拟，可调节初速度、角度和重力参数
 * @Version: 1.0
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>抛物线运动模拟器</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f0f8ff;
        }
        .container {
            display: flex;
            flex-direction: column;
            gap: 20px;
        }
        .controls {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
            padding: 15px;
            background-color: #e0f7fa;
            border-radius: 5px;
            border: 1px solid #0ABAB5;
        }
        .control-group {
            display: flex;
            flex-direction: column;
            gap: 5px;
        }
        canvas {
            border: 2px solid #0ABAB5;
            background-color: white;
            box-shadow: 0 0 10px rgba(10, 186, 181, 0.3);
        }
        label {
            font-weight: bold;
            color: #0ABAB5;
        }
        input {
            padding: 5px;
            width: 80px;
            border: 1px solid #0ABAB5;
            border-radius: 4px;
        }
        button {
            padding: 8px 15px;
            background-color: #0ABAB5;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
            transition: background-color 0.3s;
        }
        button:hover {
            background-color: #089c97;
        }
        h1 {
            color: #0ABAB5;
            text-align: center;
        }
        .info {
            background-color: #e0f7fa;
            padding: 10px;
            border-radius: 5px;
            border-left: 4px solid #0ABAB5;
        }
    </style>
</head>
<body>
    <h1>抛物线运动模拟器</h1>
    <div class="container">
        <div class="info">
            <p>调整初始速度和角度，观察抛物线轨迹变化。当前函数公式: y = v₀·sinθ·t - ½gt²</p>
        </div>
        
        <div class="controls">
            <div class="control-group">
                <label for="velocity">初始速度 (m/s):</label>
                <input type="number" id="velocity" value="20" min="1" max="100" step="1">
            </div>
            <div class="control-group">
                <label for="angle">发射角度 (度):</label>
                <input type="number" id="angle" value="45" min="0" max="90" step="1">
            </div>
            <div class="control-group">
                <label for="gravity">重力加速度 (m/s²):</label>
                <input type="number" id="gravity" value="9.8" min="1" max="20" step="0.1">
            </div>
            <button onclick="startSimulation()">开始模拟</button>
            <button onclick="resetSimulation()">重置</button>
        </div>
        
        <canvas id="parabolaCanvas" width="700" height="500"></canvas>
        
        <div class="info">
            <p id="functionDisplay">当前函数: y = 20·sin(45°)·t - ½·9.8·t²</p>
            <p id="statsDisplay">最大高度: 0m | 飞行距离: 0m | 飞行时间: 0s</p>
        </div>
    </div>

    <script>
        const canvas = document.getElementById('parabolaCanvas');
        const ctx = canvas.getContext('2d');
        const functionDisplay = document.getElementById('functionDisplay');
        const statsDisplay = document.getElementById('statsDisplay');
        
        // 模拟状态
        let animationId = null;
        let startTime = null;
        let particles = [];
        let trajectory = [];
        let maxHeight = 0;
        let flightDistance = 0;
        let flightTime = 0;
        
        // 初始绘制
        drawCoordinateSystem();
        
        function drawCoordinateSystem() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 设置坐标系参数
            const padding = 50;
            const graphWidth = canvas.width - 2 * padding;
            const graphHeight = canvas.height - 2 * padding;
            
            // 绘制坐标轴 (蒂芙尼蓝)
            ctx.strokeStyle = '#0ABAB5';
            ctx.lineWidth = 2;
            
            // X轴 (地面)
            const groundY = canvas.height - padding;
            ctx.beginPath();
            ctx.moveTo(padding, groundY);
            ctx.lineTo(padding + graphWidth, groundY);
            ctx.stroke();
            
            // Y轴
            ctx.beginPath();
            ctx.moveTo(padding, groundY);
            ctx.lineTo(padding, padding);
            ctx.stroke();
            
            // 绘制刻度
            ctx.font = '12px Arial';
            ctx.fillStyle = '#0ABAB5';
            ctx.textAlign = 'center';
            
            // X轴刻度 (距离)
            for (let x = 0; x <= graphWidth; x += 50) {
                const xPos = padding + x;
                ctx.beginPath();
                ctx.moveTo(xPos, groundY - 5);
                ctx.lineTo(xPos, groundY + 5);
                ctx.stroke();
                ctx.fillText(x + 'm', xPos, groundY + 20);
            }
            
            // Y轴刻度 (高度)
            ctx.textAlign = 'right';
            for (let y = 0; y <= graphHeight; y += 50) {
                const yPos = groundY - y;
                ctx.beginPath();
                ctx.moveTo(padding - 5, yPos);
                ctx.lineTo(padding + 5, yPos);
                ctx.stroke();
                ctx.fillText(y + 'm', padding - 10, yPos + 5);
            }
            
            // 添加标题
            ctx.font = '16px Arial';
            ctx.fillStyle = '#0ABAB5';
            ctx.textAlign = 'center';
            ctx.fillText('抛物线运动轨迹', canvas.width / 2, padding - 20);
        }
        
        function startSimulation() {
            // 停止之前的动画
            if (animationId) {
                cancelAnimationFrame(animationId);
            }
            
            // 获取输入值
            const v0 = parseFloat(document.getElementById('velocity').value);
            const angle = parseFloat(document.getElementById('angle').value);
            const g = parseFloat(document.getElementById('gravity').value);
            
            // 更新函数显示
            functionDisplay.textContent = `当前函数: y = ${v0}·sin(${angle}°)·t - ½·${g}·t²`;
            
            // 重置模拟状态
            particles = [];
            trajectory = [];
            maxHeight = 0;
            flightDistance = 0;
            flightTime = 0;
            
            // 计算初始速度分量
            const angleRad = angle * Math.PI / 180;
            const v0x = v0 * Math.cos(angleRad);
            const v0y = v0 * Math.sin(angleRad);
            
            // 计算飞行时间 (当y=0时)
            flightTime = (2 * v0y) / g;
            
            // 计算最大高度 (顶点处vy=0)
            maxHeight = (v0y * v0y) / (2 * g);
            
            // 计算飞行距离
            flightDistance = v0x * flightTime;
            
            // 更新统计信息
            statsDisplay.textContent = `最大高度: ${maxHeight.toFixed(2)}m | 飞行距离: ${flightDistance.toFixed(2)}m | 飞行时间: ${flightTime.toFixed(2)}s`;
            
            // 开始时间
            startTime = Date.now();
            
            // 添加初始粒子
            particles.push({
                x: 0,
                y: 0,
                vx: v0x,
                vy: v0y,
                time: 0,
                color: '#0ABAB5'
            });
            
            // 开始动画
            animate();
        }
        
        function animate() {
            const currentTime = Date.now();
            const elapsedTime = (currentTime - startTime) / 1000; // 转换为秒
            
            // 如果超过飞行时间，停止动画
            if (elapsedTime > flightTime) {
                return;
            }
            
            // 获取参数
            const g = parseFloat(document.getElementById('gravity').value);
            
            // 清除画布
            drawCoordinateSystem();
            
            // 更新和绘制粒子
            for (let i = 0; i < particles.length; i++) {
                const p = particles[i];
                
                // 更新位置 (x = v0x * t, y = v0y * t - 0.5 * g * t^2)
                p.time = elapsedTime;
                p.x = p.vx * p.time;
                p.y = p.vy * p.time - 0.5 * g * p.time * p.time;
                
                // 记录轨迹点
                if (i === 0) { // 只记录第一个粒子的轨迹
                    trajectory.push({x: p.x, y: p.y});
                }
                
                // 绘制粒子
                ctx.fillStyle = p.color;
                ctx.beginPath();
                ctx.arc(
                    50 + p.x,  // x位置 (加上padding)
                    450 - p.y,  // y位置 (canvas坐标系反转)
                    5,          // 半径
                    0,          // 起始角度
                    Math.PI * 2 // 结束角度
                );
                ctx.fill();
            }
            
            // 绘制轨迹线
            if (trajectory.length > 1) {
                ctx.strokeStyle = 'rgba(10, 186, 181, 0.5)';
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.moveTo(50 + trajectory[0].x, 450 - trajectory[0].y);
                
                for (let i = 1; i < trajectory.length; i++) {
                    ctx.lineTo(50 + trajectory[i].x, 450 - trajectory[i].y);
                }
                ctx.stroke();
            }
            
            // 继续动画
            animationId = requestAnimationFrame(animate);
        }
        
        function resetSimulation() {
            // 停止动画
            if (animationId) {
                cancelAnimationFrame(animationId);
                animationId = null;
            }
            
            // 重置状态
            particles = [];
            trajectory = [];
            maxHeight = 0;
            flightDistance = 0;
            flightTime = 0;
            
            // 重置显示
            functionDisplay.textContent = `当前函数: y = v₀·sinθ·t - ½gt²`;
            statsDisplay.textContent = `最大高度: 0m | 飞行距离: 0m | 飞行时间: 0s`;
            
            // 重绘坐标系
            drawCoordinateSystem();
        }
    </script>
</body>
</html>